package org.youyun56.gpsserver.net;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import net.sf.ehcache.search.Result;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.youyun56.gpsserver.CacheFactory;
import org.youyun56.gpsserver.Config;
import org.youyun56.gpsserver.Task;
import org.youyun56.gpsserver.command.Command;
import org.youyun56.gpsserver.command.CommandFactory;
import org.youyun56.gpsserver.command.CommandType;
import org.youyun56.gpsserver.command.JSONKey;
import org.youyun56.gpsserver.common.SessionKey;
import org.youyun56.gpsserver.common.SessionManager;
import org.youyun56.gpsserver.common.StringUtils;
import org.youyun56.gpsserver.db.DBHeper;
import org.youyun56.gpsserver.entity.Car;
import org.youyun56.gpsserver.entity.Good;
import org.youyun56.gpsserver.entity.Im;

public class GpsHandler extends IoHandlerAdapter {
    private final static Logger log = LoggerFactory.getLogger(GpsHandler.class);
    /**
     * 手机GPS定位
     */
    private final static int gpsLocation = 4;
    public final static CopyOnWriteArraySet<IoSession> sessions = new CopyOnWriteArraySet<IoSession>();
    public final static CopyOnWriteArraySet<String> users = new CopyOnWriteArraySet<String>();
    private final static ThreadPoolExecutor pool = new ThreadPoolExecutor(Integer.parseInt(CacheFactory.props
            .getProperty(Config.HttpThread)), Integer.parseInt(CacheFactory.props.getProperty(Config.HttpThread)), 0L,
            TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()) {
        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            // TODO Auto-generated method stub
            super.afterExecute(r, t);
            Command command = null;
            try {
                command = (Command) ((FutureTask<?>) r).get();
                IoSession session = command.getSession();
                if (Command.GetGoods == command) {
                    SessionManager.changeCity(command.getParameters().getInt("city"), session);
                    List<Result> rss = (List<Result>) command.getOutput();
                    JSONObject result = new JSONObject();
                    result.put(JSONKey.ID, CommandType.GetGoods);
                    List<JSONObject> goods = new ArrayList<JSONObject>();
                    int i = 1;
                    if (rss != null) {
                        for (int index = 0; index < rss.size(); index++) {
                            Good good = (Good) rss.get(index).getValue();
                            JSONObject o = CommandFactory.goodJson(good);
                            goods.add(o);
                            if (i++ % 3 == 0) {
                                result.put(JSONKey.GOODS, goods);
                                session.write(result.toString().replace("\n", ""));
                                result = new JSONObject();
                                result.put(JSONKey.ID, CommandType.GetGoods);
                                goods = new ArrayList<JSONObject>();
                            }
                        }
                    }
                    if (!goods.isEmpty()) {
                        result.put(JSONKey.GOODS, goods);
                        session.write(result.toString().replace("\n", ""));
                    }

                } else if (Command.AppendGoodEnd == command) {
                    SessionManager.changeCity(command.getParameters().getInt("city"), session);
                    List<Result> rss = (List<Result>) command.getOutput();
                    JSONObject result = new JSONObject();
                    result.put(JSONKey.ID, CommandType.Append_Good_END);
                    List<JSONObject> goods = new ArrayList<JSONObject>();
                    int i = 1;
                    if (rss != null) {
                        for (int index = 0; index < rss.size(); index++) {
                            Good good = (Good) rss.get(index).getValue();
                            JSONObject o = CommandFactory.goodJson(good);
                            goods.add(o);
                            if (i++ % 3 == 0) {
                                result.put(JSONKey.GOODS, goods);
                                session.write(result.toString().replace("\n", ""));
                                result = new JSONObject();
                                result.put(JSONKey.ID, CommandType.Append_Good_END);
                                goods = new ArrayList<JSONObject>();
                            }
                        }
                    }
                    if (!goods.isEmpty()) {
                        result.put(JSONKey.GOODS, goods);
                        session.write(result.toString().replace("\n", ""));
                    }

                } else if (command == Command.GoodSearch) {
                    SessionManager.changeCity(command.getParameters().getInt("targetCity"), session);
                    List<Result> rss = (List<Result>) command.getOutput();
                    JSONObject result = new JSONObject();
                    result.put(JSONKey.ID, CommandType.Good_Search);
                    List<JSONObject> goods = new ArrayList<JSONObject>();
                    int i = 1;
                    if (rss != null) {
                        for (int index = 0; index < rss.size(); index++) {
                            Good good = (Good) rss.get(index).getValue();
                            JSONObject o = CommandFactory.goodJson(good);
                            goods.add(o);
                            if (i++ % 3 == 0) {
                                result.put(JSONKey.GOODS, goods);
                                session.write(result.toString().replace("\n", ""));
                                result = new JSONObject();
                                result.put(JSONKey.ID, CommandType.Good_Search);
                                goods = new ArrayList<JSONObject>();
                            }
                        }
                    }
                    if (!goods.isEmpty()) {
                        result.put(JSONKey.GOODS, goods);
                        session.write(result.toString().replace("\n", ""));
                    }

                } else if (command == Command.GoodSearchEnd) {
                    SessionManager.changeCity(command.getParameters().getInt("city"), session);
                    List<Result> rss = (List<Result>) command.getOutput();
                    JSONObject result = new JSONObject();
                    result.put(JSONKey.ID, CommandType.Good_Search_END);
                    List<JSONObject> goods = new ArrayList<JSONObject>();
                    int i = 1;
                    if (rss != null) {
                        for (int index = 0; index < rss.size(); index++) {
                            Good good = (Good) rss.get(index).getValue();
                            JSONObject o = CommandFactory.goodJson(good);
                            goods.add(o);
                            if (i++ % 3 == 0) {
                                result.put(JSONKey.GOODS, goods);
                                session.write(result.toString().replace("\n", ""));
                                result = new JSONObject();
                                result.put(JSONKey.ID, CommandType.Good_Search_END);
                                goods = new ArrayList<JSONObject>();
                            }
                        }
                    }
                    if (!goods.isEmpty()) {
                        result.put(JSONKey.GOODS, goods);
                        session.write(result.toString().replace("\n", ""));
                    }
                } else if (Command.ReConnect == command) {
                    Car car = (Car) command.getOutput();
                    if (car == null) {
                        command.getSession().close(true);
                    } else {
                        String focusCity = command.getParameters().get("focusCity");
                        if (StringUtils.isNotEmpty(focusCity)) {
                            session.setAttribute(SessionKey.CAR, car);
                            SessionManager.addSession(Integer.valueOf(focusCity), session, car.getCarMobile());
                            List<Result> rss = CacheFactory.instance.getGoodCache(Integer.valueOf(focusCity), command
                                    .getParameters().getLong("index"));
                            JSONObject result = new JSONObject();
                            result.put(JSONKey.ID, CommandType.Re_Connect);
                            List<JSONObject> goods = new ArrayList<JSONObject>();
                            int i = 0;
                            if (goods != null) {
                                for (int index = 0; index < rss.size(); index++) {
                                    Good good = (Good) rss.get(index).getValue();
                                    JSONObject o = CommandFactory.goodJson(good);
                                    goods.add(o);
                                    if (i++ % 3 == 0) {
                                        result.put(JSONKey.GOODS, goods);
                                        session.write(result.toString().replace("\n", ""));
                                        result = new JSONObject();
                                        result.put(JSONKey.ID, CommandType.Re_Connect);
                                        goods = new ArrayList<JSONObject>();
                                    }
                                }
                            }
                            if (!goods.isEmpty()) {
                                result.put(JSONKey.GOODS, goods);
                                session.write(result.toString().replace("\n", ""));
                            }
                        }
                    }
                } else if (Command.LOGIN == command) {
                    Car car = (Car) command.getOutput();
                    if (car == null) {
                        session.write(CommandFactory.login(false, "用户名或密码错误.").toString().replace("\n", ""));
                    } else {
                        String cversion = command.getParameters().get("version");
                        String version = GpsServer.props.getProperty("version");
                        if (!cversion.equalsIgnoreCase(version)) {
                            JSONObject o = CommandFactory.login(false, "版本过期,请下载最新版本");
                            o.put("version", version);
                            session.write(o);
                            return;
                        }
                        String imsi = command.getParameters().get("imsi");
                        if (car.getImsi().length() > 0 && !car.getImsi().equals(imsi)) {
                            JSONObject o = CommandFactory.login(false, "该手机已被其它号码绑定");
                            o.put("imis", "");
                            session.write(o);
                            return;
                        }
                        if (StringUtils.isEmpty(car.getImsi()) && StringUtils.isNotEmpty(imsi)) {
                            DBHeper.carDao.updateCarLoginInfo(session.getRemoteAddress(), car.getId(), cversion, imsi);
                        }
                        session.setAttribute(SessionKey.CAR, car);
                        SessionManager.addSession(car.getCityId(), session, car.getCarMobile());
                        JSONObject o = CommandFactory.login(true, null);
                        o.put(SessionKey.CAR, car);
                        if (car.getPartner() == gpsLocation) {
                            o.put("gpsinterval", Integer.valueOf(GpsServer.props.getProperty("gpsinterval", "30")));
                        }
                        ConcurrentLinkedQueue<Im> ims = CacheFactory.instance.getCacheIms(car);
                        if (ims != null && !ims.isEmpty()) {
                            JSONArray array = new JSONArray();
                            int i = 1;
                            while (!ims.isEmpty()) {
                                array.add(JSONObject.fromObject(ims.poll()));
                                if (i++ % 3 == 0) {
                                    o.put("ims", array);
                                    session.write(o.toString().replace("\n", ""));
                                    o = CommandFactory.login(true, null);
                                }
                            }
                            if (!array.isEmpty()) {
                                o.put("ims", array);
                                session.write(o.toString().replace("\n", ""));
                            }
                        } else {
                            session.write(o.toString().replace("\n", ""));
                        }
                    }
                } else if (command == Command.ChangeCarHearvy) {
                    session.write(command.getOutput());
                } else if (command == Command.UserOut) {
                    SessionManager.removeSession(session);
                    session.close(true);
                } else if (command == Command.UpdateCarStatus) {
                    Car car = (Car) session.getAttribute(SessionKey.CAR);
                    session.write(CommandFactory.updateCar(car));
                } else if (command == Command.UploadCarGpsLocation) {

                } else {
                    session.write(CommandFactory.INVALID_COMMAND.toString().replace("\n", ""));
                }

            } catch (Exception e) {
                // TODO: handle exception
                log.error(String.format("input %s occur %s", command.getInput(), e.getMessage()), e);
            }

        }
    };

    @Override
    public void exceptionCaught(IoSession session, Throwable cause) {
        Car car = (Car) session.getAttribute(SessionKey.CAR);
        log.error(String.format("Exception Out,User %s:%s", session.getRemoteAddress(), car != null ? car.toString()
                : "Not Login Yet"), cause);
        session.close(true);
    }

    @Override
    public void messageSent(IoSession session, Object message) throws Exception {
        // TODO Auto-generated method stub
        super.messageSent(session, message);
        log.info(message.toString().getBytes("utf-8").length + "  " + message.toString());
    }

    @Override
    public void messageReceived(IoSession session, Object message) {
        log.info(message.toString());
        Task task = Command.getExecute(message.toString());
        if (task != null) {
            task.getCommand().setSession(session);
            pool.submit(task);
        } else {
            log.error(String.format("%s post error command %s", session.getRemoteAddress().toString(), message));
        }

    }

    public void broadcast(String message) {
        synchronized (sessions) {
            for (IoSession session : sessions) {
                if (session.isConnected()) {
                    session.write("BROADCAST OK " + message);
                }
            }
        }
    }

    @Override
    public void sessionClosed(IoSession session) throws Exception {
        SessionManager.removeSession(session);
        Car car = (Car) session.getAttribute(SessionKey.CAR);
        log.info(String.format("%s %s Closed", car != null ? car.getCarId() : "", session.getRemoteAddress().toString()));
    }

    @Override
    public void sessionOpened(IoSession session) throws Exception {
        log.info(String.format("Client %s Connected", session.getRemoteAddress().toString()));
    }

    public boolean isChatUser(String name) {
        return users.contains(name);
    }

    public int getNumberOfUsers() {
        return users.size();
    }

    public void kick(String name) {
        synchronized (sessions) {
            for (IoSession session : sessions) {
                if (name.equals(session.getAttribute("user"))) {
                    session.close(true);
                    break;
                }
            }
        }
    }
}
